package munci.old.know;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.Reader;

import munci.old.know.words.Word;
import munci.old.learn.Basics;
import munci.old.learn.Control;
import munci.old.learn.Generics;
import munci.old.learn.Literals;
import munci.old.learn.Members;
import munci.old.learn.Statics;
import munci.old.use.Try;
import munci.old.use.Visitor;
import munci.read.Token;
import munci.read.Tokenizer;


public class Env {
	
	private static final ThreadLocal<Env> local = new ThreadLocal<Env>() {
		@Override protected Env initialValue() {
			return new Env();
		}
	};
	
	public static Env get() {
		return local.get();
	}
	
	public final Dictionary dict = new Dictionary(ClassLoader.getSystemClassLoader(), "monkey");
	public final Stack stack = new Stack();
	
	private Tokenizer tokens;
	private Token tok;
	
	private boolean compiling = false;
	
	private Word executing = null;
	
	public void nextToken() {
		tok = null;
	}
	
	public Token token() {
//		if (tok == null)
//			tok = tokens.getNextToken();
		return tok;
	}
	
	public void setCompiling(boolean val) {
		this.compiling = val;
	}
	
	public boolean compiling() {
		return compiling;
	}
	
	public boolean immediately(String... names) {
		if (!compiling()) return false;
		compiling = false;
		try {
			call(names);
		} finally {
			compiling = true;
		}
		return true;
	}

	public void call(final String... names) {
		for (final String name : names) {
			if (!dict.visit(new Visitor<Word>() {
				@Override public boolean visit(Word word) {
					if (word.name.equals(name)) {
						Word tmp = executing;
						executing = word;
						try {
							return word.execute(Env.this);
						} catch (RuntimeException e) {
							throw e;
						} catch (Throwable e) {
							throw Try.except("Calling: " + word.name, e);
						} finally {
							executing = tmp;
						}
					}
					return false;
				}
			}))
				throw Try.except("Word not found: " + name);
		}
	}
	
	public void interpret(Tokenizer toks) {
		Tokenizer prev = tokens;
		tokens = toks;
		try {
			nextToken();
			Token tok;
			while (null != (tok = token())) {
				if (tok instanceof Token.Literal) {
					stack.push(tok, tok.getClass());
				}
				nextToken();
				call(tok.id());
			}
		} finally {
			tokens = prev;
		}
	}
	
	@Override public String toString() {
		StringBuilder str = new StringBuilder();
		str.append(compiling()? "compiling" : "interpret");
		if (executing != null) {
			str.append(' ').append(executing.name);
		}
		return str.toString();
	}
	
	public void learn() {
		dict.learn(Basics.class).learn(Literals.class)
				.learn(Generics.class).learn(Statics.class)
				.learn(Members.class).learn(Control.class);
	}
	
	public static void main(String[] args) throws FileNotFoundException {
		Env.get().learn();

		final String name = "basic.mky";
		Reader input = new BufferedReader(new InputStreamReader(
					ClassLoader.getSystemResourceAsStream(name)));
		
//		Env.get().interpret(new Tokenizer(input, name));
	}
}
